package gateway.behaviours;

import gateway.GatewayAgent;
import gateway.ontology.classes.basic_translation.ParamNotFoundException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;

import com.CharNotValidException;
import com.Constants;
import com.Event;
import com.ParamsLabel;
import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import com.sun.spot.util.Utils;
import com.sun.squawk.util.StringTokenizer;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;

public class CommunicationChannelReceiverBehaviour extends CyclicBehaviour {

	private RadiogramConnection conn = null;
	private String communicationPort;

	public CommunicationChannelReceiverBehaviour(String communicationPort,
			Agent agent) {
		super(agent);
		this.communicationPort = communicationPort;
	}

	@Override
	public void onStart() {
		try {
			conn = (RadiogramConnection) Connector.open("radiogram://:"
					+ this.communicationPort);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void action() {

		if (conn != null) {
			try {
				Datagram dg = conn.newDatagram(conn.getMaximumLength());
				conn.receive(dg);
				this.parseDatagram(dg);
			} catch (IOException ioe) {
				// TODO should we care?
			}
		} else myAgent.doDelete();
	}

	@Override
	public int onEnd() {
		try {
			if (conn != null) {
				conn.close();
			}
		} catch (IOException e) {
			// should we care?
		}
		return super.onEnd();
	}

	private void parseDatagram(Datagram dg) throws IOException {
		GatewayAgent gateway = (GatewayAgent) this.myAgent;
		String event = dg.readUTF();
		StringTokenizer eventToken = new StringTokenizer(event,
				Constants.COMMUNICATION_SEPARATOR);
		byte evtName = -1;
		if (eventToken.hasMoreTokens()) {
			evtName = Byte.parseByte(eventToken.nextToken());
			switch (evtName) {
			case Event.MGR_REQUEST:
			case Event.MGR_ACK:
			case Event.MGR_EXECUTED:
				// do nothing
				break;
			case Event.DSC_PUBLISH:
				String addressPublish = dg.getAddress();
				DiscoveryAnswerBehaviour discoveryAnswer = new DiscoveryAnswerBehaviour(
						gateway, this.communicationPort, addressPublish);
				myAgent.addBehaviour(gateway.getTBF().wrap(discoveryAnswer));
				break;
			case Event.DSC_ANSWER:
			case Event.DSC_REFRESH:
				String address = dg.getAddress();
				ArrayList<String> agents = new ArrayList<String>();
				while (eventToken.hasMoreTokens()) {
					String agent = eventToken.nextToken();
					agents.add(agent);
					System.out.println("Gateway - adding " + agent);
				}
				gateway.getNaming().addSpotAgents(address, agents);
				break;

			case Event.AGN_CREATION:
				break;
			case Event.MSG:
			default:
				if (eventToken.hasMoreTokens()) {
					String stringEvent = eventToken.nextToken();
					Event eventMsg = new Event(stringEvent);
					route(eventMsg.getSourceID(),
							eventMsg.getTargetID(), eventMsg, true);
				}
				break;
			}
		}
	}
	
	public void route(String source, String target, Event event, boolean local) {
		GatewayAgent gateway = (GatewayAgent) this.myAgent;
		if (target.equals(Constants.BROADCAST)) {
			if (!local) {
				List<String> neighbors = gateway.getNaming().getNeighborsAddress();
				for (int i = 0; i < neighbors.size(); i++) {
					try {
						String address = (String) neighbors.get(i);
						Event tmessage = event.copy();
						tmessage.setParam(ParamsLabel.EVT_ADDRESS, address);
						gateway.getChannelSender().put(tmessage);
					} catch (CharNotValidException e) {
						e.printStackTrace();
					}
				}
			}
		} else {
			String address = gateway.getNaming().getAddress(target);
			if (!address.equals("")) {
				try {
					event.setParam(ParamsLabel.EVT_ADDRESS, address);
					gateway.getChannelSender().put(event);
				} catch (CharNotValidException e) {
					e.printStackTrace();
				}
			} else {
				AID targetAID = gateway.getLocalJADEAgent(target);
				if (targetAID != null) {
					gateway.getACLMessageSender().put(event, targetAID);
				}
			}
		}
	}
}

class DiscoveryAnswerBehaviour extends OneShotBehaviour {

	private GatewayAgent gateway;
	private String address;

	public DiscoveryAnswerBehaviour(GatewayAgent gateway,
			String communicationPort, String address) {
		this.gateway = gateway;
		this.address = address;
	}

	@Override
	public void action() {
		gateway.getNaming().addSpotAgents(address, new ArrayList<String>());

		Utils.sleep((new Random())
				.nextInt(Constants.MAX_DISCOVERY_ANSWER_WAITING_TIME));
		Event event = new Event(gateway.getIEEAddress().asDottedHex(),
				this.address, Event.DSC_ANSWER, Event.NOW);
		gateway.getChannelSender().put(event);
	}
}